home *** CD-ROM | disk | FTP | other *** search
- page 60,132
-
- title RAMHAM Hammer the PE out of RAM by Clifford A McCullough
- subttl Version 1.00 December 15, 1985
-
- comment * RamHammer is intended to discover latent parity errors.
- This is done by cyclicly writing a pattern to memory and then checking
- that the pattern remains. The pattern is checked several times before
- a new pattern is written. This requires the RAM to maintain the
- pattern for a period of time. The pattern progresses through each
- block so that each byte is tested with all patterns and each bit is
- tested both high and low.
-
- RamHammer checks that it resides completely within the lowest 64K
- of memory. For this reason the system should be booted with a minimum
- of resident user programs (if any). RamHammer will not check this
- first block of memory. Some other method must be used to check this
- block if it is suspected.
-
- When RamHammer detects a memory error, it sends to the screen a
- message including the time, the memory address, and the bit that
- was found to be in error. The ports and data bytes it uses to disable
- the NMI and parity reporting mechanism is specific to the IBM PC and
- may not be correct for "compatible" machines.
- *
-
- .radix 16
-
- ;***** Equates *********************************************************
-
- check_no equ 0FF ;number of check cycles
- begin_blk equ 004 ;beginning block to be checked
- end_blk equ 040 ;last block to be checked
- nmi_port equ 0A0 ;NMI on / off register
- nmi_on equ 080 ;turn NMI on
- nmi_off equ 000 ;turn NMI off
- port_b equ 061 ;hardware port B
- port_c equ 062 ;hardware port C
- par_err equ 0C0 ;parity error bits - either brd
- par_err_m equ 080 ;parity error bit for main brd
- par_err_e equ 040 ;parity error bit for exp brd
- clr_pe equ 030 ;bits to clear parity error latches
- stack_size equ 0100 ;minimum size of program stack
- cr equ 0Dh ;carriage return character
- lf equ 0A ;line feed character
- bel equ 07 ;bell character
-
- ;***** Main Program ****************************************************
-
- code segment
- assume cs:code, ds:code, es:code, ss:code
- org 100 ;where all good .COM programs go
-
- ;----- Start: make some checks and set up stuff -----------------------
-
- start proc near
-
- ;check the address of the end of the program
- mov dx,offset too_big_msg ;get error message
- mov ax,cs ;get program segment
- cmp ax,1000 ;check for 1st 64K
- jge bye_bye ;/
- mov cl,4 ;convert from this seg to abs number
- shl ax,cl ;/
- add ax,offset prog_end ;add in the length of the prog
- jc bye_bye ;check program size
- add ax,stack_size ;add in the length of the stack
- jc bye_bye ;check program size
-
- ;find end of user memory and set up block enable flags
- int 12 ;get memory size in K bytes
- mov dx,offset too_small_msg ;get next error message
- cmp ax,4 ;check if total memory > 64K
- jng bye_bye ;/
- mov cl,4 ;convert to base 16K
- shr ax,cl ;/
- sub ax,4 ;skip 1st 4 blocks
- mov cx,ax ;set up block counter
- cld ;set direction to increment
- mov al,0FF ;get the block enable flag
- mov di,offset en_flags + 4 ;point to enable flags after 1st 4
- repz stosb ;set flag for each block existing
-
- ;check for reduced hammering area
- mov al,00 ;get block disable flag
- std ;set direction to decrement
- mov cx,040d ;get last block
- sub cx,end_blk ;any work to do?
- jle lower ;/
- mov di,offset en_flags +39d ;point to enable flags
- repz stosb ;reset flags
- lower:
- cld ;set direction to increment
- mov cx,begin_blk ;get starting point
- sub cx,04 ;any work to do?
- jle safe_stack ;/
- mov di,offset en_flags + 4 ;point to enable flags
- repz stosb ;reset flags
-
- ;load a "safe" stack pointer
- safe_stack:
- mov ax,cs ;get program segment
- mov cl,4 ;convert from seg to abs
- shl ax,cl ;/
- neg ax ;get difference from 64K
- mov sp,ax ;load safe stack pointer
-
- ;turn off NMI - parity error interrupt
- mov al,nmi_off ;get off byte
- out nmi_port,al ;turn off nmi
-
- ;start hammering RAM
- sub dl,dl ;start pattern at 0
- jmp control_loop
-
- ;installation error - either too big system or too small memory.
- ;print message and exit.
- bye_bye:
- mov ah,9 ;print message
- int 21 ;/
- int 20 ;bye bye
-
- too_big_msg db cr, lf
- db "Program is not contained in first 64K of memory."
- db cr, lf, bel
- db "Reduce system overhead and try again."
- db cr, lf, "$"
-
- too_small_msg db cr, lf
- db "User memory is not more than 64K bytes."
- db cr, lf, bel
- db "Nothing remains to be checked."
- db cr, lf, "$"
-
- en_flags db 40d dup(0) ;40 block enable flags (cleared)
- pattern db 10110010b ;par = e, B2
- db 11011001b ;par = o, D9
- db 01101100b ;par = e, 6C
- db 10110110b ;par = o, B6
- db 01011011b ;par = o, 5B
- db 00101101b ;par = e, 2D
- db 10010110b ;par = e, 96
- db 11001011b ;par = o, CB
- db 01100101b ;par = e, 65
- pat_length equ this byte - pattern - 1 ;length of test pattern - 1
-
- start endp
-
- ;----- Control Loop ----------------------------------------------------
-
- control_loop proc near
- ;enter: dl = pattern pointer
-
- ;set up pattern pointer to new value
- dec dl ;start at different spot than last
- jge write_pattern ;check for wrap-around
- inc loop_counter ;count loops
- mov dx,loop_counter ;update printout
- mov si,offset loop_hex ;load storage location
- mov bx,offset hex_tbl ;point to hex to ascii xlat table
- call hex2ascii ;convert
- mov dl,pat_length ;reset the pattern pointer
-
- write_pattern:
- ;find first enabled block
- sub bx,bx ;start at beginning
- call find_next_block ;find the first enabled block
- jc exit ;no blocks enabled
-
- ;pattern write
- blk_write:
- call pat_write ;do the writing
- call find_next_block ;find the next enabled block
- jnc blk_write ;a block was found
-
- ;all enabled blocks written with pattern
- mov dh,check_no ;load number of times to check
- cycle:
-
- ;find first enabled block
- sub bx,bx ;start at beginning
- call find_next_block ;find the first enabled block
- jc exit ;should not happen
-
- ;pattern check
- blk_check:
- call pat_check ;do the checking
- call find_next_block ;find the next enabled block
- jnc blk_check ;a block was found
-
- ;all enabled blocks checked and disabled if faulty
- call pacifier ;look busy
- dec dh ;decrement check counter
- jnz cycle ;check for more checking
- jmp control_loop
-
- ;no more blocks enabled for checking
- exit:
- mov dx,offset exit_msg ;print exit message
- mov ah,9 ;/
- int 21 ;/
- int 20 ;bye bye
-
- exit_msg db cr, lf
- db "All memory blocks checked have errors."
- db cr, lf, bel
- db "Execution terminated."
- db cr, lf, "$"
-
- loop_counter dw 0FFFF ;over all loop counter
- loop_hex db "0000-" ;ascii of loop counter
- pac_hex db "0000$" ;ascii of pacifier
-
- control_loop endp
-
- ;***** Subroutines *****************************************************
-
- ;----- Pacifier --------------------------------------------------------
-
- pacifier proc near
- ;enter: dh = check counter; dl = pattern pointer
- ;exit: ax, cx, bp, si = lost
-
- push bx ;save block number
- mov bp,dx ;save counter and pointer
-
- ;get current cursor position
- mov ah,3 ;read cursor position
- sub bh,bh ;/ page number = 0
- int 10 ;/
- push dx ;save current cursor position
-
- ;set cursor position
- mov ah,2 ;set cursor position
- mov dh,0 ;/ row = 1
- mov dl,69d ;/ column = 70
- int 10 ;/ page number = 0
-
- ;convert pacifier to ascii
- mov dx,bp ;restore counter and pointer
- mov si,offset pac_hex ;point to storage location
- mov bx,offset hex_tbl ;point to translation table
- call hex2ascii ;make conversion
-
- ;print pacifier
- mov dx,offset loop_hex ;get start of message
- mov ah,9 ;print message
- int 21 ;/
-
- ;restore stuff
- mov ah,2 ;restore cursor position
- sub bh,bh ;/ page number = 0
- pop dx ;/ get old setting
- int 10 ;/
- mov dx,bp ;restore counter and pointer
- pop bx ;restore block number
- ret
-
- pacifier endp
-
- ;----- find_next_block -------------------------------------------------
-
- find_next_block proc near
- ;find the next enabled block after the one pointed to by bx and
- ;set up es to point to the block of memory found
- ;enter: bx = current block number
- ;exit: bx = next block number; es = next block segment address
- ; cx = lost; si = lost
- ; cy set = end of blocks reached; cy clear = next block found
-
- mov cx,length en_flags - 1 ;total number of blocks - 1
- sub cx,bx ;set count to remaining blocks
- jz none_found ;already at end of blocks
- next_blk:
- inc bx ;point to next block
- cmp byte ptr en_flags[bx],0FF ;is it enabled?
- je found_blk ;/ yes - found a good block
- loop next_blk ;/ no - check next
- none_found:
- stc ;set end of blocks flag
- ret
-
- found_blk:
- mov si,bx ;do not disturb bx
- mov cl,6 ;convert si to seg addr
- ror si,cl ;/
- mov es,si ;es points to 16K memory block
- clc ;set found block flag
- ret
-
- find_next_block endp
-
- ;----- pat_write -------------------------------------------------------
-
- pat_write proc near
- ;do the actual pattern writing
- ;enter: dl = pattern start pointer; es = segment address to put pattern in
- ;exit: ax, cx, si, di = lost
-
- mov cx,4000 ;set counter to block size
- sub di,di ;clear destination index
- mov al,dl ;get pattern start pointer
- cbw ;clear high byte
- add ax,offset pattern ;complete index addr to pattern
- mov si,ax ;set source index
- write_next:
- movsb ;put pattern byte in block
- cmp si,offset pattern + pat_length ;check source index
- jle skip2 ;if necessary ...
- mov si,offset pattern ;/ reset pattern pointer
- skip2:
- loop write_next ;write next byte
- ret
-
- pat_write endp
-
- ;----- pat_check -------------------------------------------------------
-
- pat_check proc near
- ;do the actual pattern checking
- ;enter: dl = pattern start pointer; es = segment address of pattern
- ; bx = current block number
- ;exit: ax, cx, si, di = lost
- ; if error found: control is passed to bad_byte procedure
- ; di = bad byte index + 1; si = pattern index + 1
-
- mov cx,4000 ;set counter to block size
- sub di,di ;clear destination index
- mov al,dl ;get pattern start pointer
- cbw ;clear high byte
- add ax,offset pattern ;complete index addr to pattern
- mov si,ax ;set source index
- check_next:
- cmpsb ;check pattern byte in block
- jne bad_byte ;oops!
- in al,port_c ;check for parity error
- test al,par_err ;/
- jnz bad_byte ;oops!
- cmp si,offset pattern + pat_length ;check source index
- jle skip3 ;if necessary ...
- mov si,offset pattern ;/ reset pattern pointer
- skip3:
- loop check_next ;check next byte
- ret
-
- pat_check endp
-
- ;----- bad_byte --------------------------------------------------------
-
- bad_byte proc near
- ;a bad byte was found in memory. report it and disable the block
- ;enter: bx = current block number; es = current block segment address
- ; di = bad byte index + 1; si = pattern index + 1
- ;exit: ax, cx, si = lost
-
- push dx ;save pattern start pointer
- mov dx,offset crlf ;print a new line
- mov ah,9 ;/
- int 21 ;/
-
- ;check for source of error
- dec di ;correct for last increment
- dec si ;/
- cmp si,offset pattern ;check source index
- jge skip4 ;if necessary ...
- mov si,offset pattern + pat_length ;/ reset pattern pointer
- skip4:
- mov al,es:[di] ;get bad byte
- xor al,[si] ;check for a bad bit
- jnz bit_error ;/
- in al,port_c ;get parity error bits
- mov dx,offset mpe_msg ;get main pe message
- test al,par_err_m ;check for main brd parity error
- jnz err_recover ;/
- mov dx,offset epe_msg ;get expansion pe message
- test al,par_err_e ;check for exp brd parity error
- jnz err_recover ;/
-
- ;error detected by pat_check but the source was not located.
- ;print message but do not disable this block. it will be checked again.
- mov dx,offset shit_msg ;print the message ...
- jmp short noerr_recover ;/ do not disable the block
-
- bit_error:
- ;a bit error was located. separate the bits and include in the message.
- mov si,offset err_byte + 7 ;get storage location
- mov cx,8 ;get count
- store_bit:
- mov ah,018 ;get ascii "0" pre-shifted
- shr al,1 ;put bit in carry
- rcl ah,1 ;get bit from carry
- mov [si],ah ;store ascii of bit
- dec si ;point to next bit
- loop store_bit ;do all bits
- mov dx,offset bit_msg ;get bit error message
-
- err_recover:
- mov byte ptr en_flags[bx],00 ;disable this block
- noerr_recover:
- mov ah,9 ;DOS print message function
- int 21 ;/
- call get_time ;get time and include in status_msg
- push bx ;save block number
- mov bx,offset hex_tbl ;point to hex to ascii xlat table
- mov dx,es ;separate block segment, convert
- mov si,offset err_blk ;/ to ascii, and include in
- call hex2ascii ;/ status_msg
- mov dx,di ;separate memory address, convert
- mov si,offset err_addr ;/ to ascii, and include in
- call hex2ascii ;/ status_msg
- mov dx,offset status_msg ;print status message
- mov ah,9 ;/
- int 21 ;/
- pop bx ;restore block number
- pop dx ;restore pattern start pointer
-
- ;clear parity error latches
- in al,port_b ;get current status of port B
- or al,clr_pe ;clear parity latches
- out port_b,al ;/
- xor al,clr_pe ;re-enable parity latches
- out port_b,al ;/
- ret
-
- crlf db cr, lf, "$"
- shit_msg db "An error was detected but not located.$"
- bit_msg db "The bit indicated was found to be in error: "
- err_byte db "00000000$"
- mpe_msg db "The main board parity bit was found to be in error.$"
- epe_msg db "The expansion board parity bit was found to be in "
- db "error.$"
- status_msg db cr, lf
- db "Time: "
- err_hour db "00:"
- err_min db "00 Memory Segment: "
- err_blk db "0000 Memory Address: "
- err_addr db "0000", cr, lf, "$"
-
- bad_byte endp
-
- ;----- get_time --------------------------------------------------------
-
- get_time proc near
- ;get the time of day, make decimal, and put it in status_msg.
- ;exit: ax, cx = lost
-
- mov ah,2C ;DOS get time function
- int 21 ;/
- mov al,ch ;hours (0-23)
- call hex2dec ;convert hex to ascii decimal
- mov word ptr err_hour,ax ;store ascii hours
- mov al,cl ;minutes (0-59)
- call hex2dec ;convert hex to ascii decimal
- mov word ptr err_min,ax ;store ascii minutes
- ret
-
- get_time endp
-
- ;----- hex2dec ---------------------------------------------------------
-
- hex2dec proc near
- ;convert a hex number <100 in al into ascii decimal in ax.
- ;enter: al = number to be converted
- ;exit: ah = ones; al = tens; ch = lost
-
- cbw ;clear high byte
- mov ch,10d ;divide ax by 10
- div ch ;/
- add ax,"00" ;make ascii
- ret
-
- hex2dec endp
-
- ;----- hex2ascii -------------------------------------------------------
-
- hex2ascii proc near
- ;convert a hex number in dx into ascii. si points to storage location.
- ;enter: dx = number to be converted; si = storage location pointer
- ; bx = offset of hex_tbl
- ;exit: ah, cl = lost
-
- add si,3 ;start at end of number
- mov ah,dl ;do it to low byte
- call hex_lookup ;/
- mov ah,dh ;do it to high byte
- call hex_lookup ;/
- ret
-
- hex2ascii endp
-
- ;----- hex_lookup ------------------------------------------------------
-
- hex_lookup proc near
- ;separate two hex digits in ah, convert to ascii and store
- ;enter: ah = two hex digits; si = storage location
- ; bx = hex_tbl offset
- ;exit: al, cl = lost; si = decremented by two
-
- mov al,ah ;get low byte
- and al,0F ;get low nibble
- xlat hex_tbl ;translate hex to ascii
- mov [si],al ;store 1st/3rd digit
- dec si ;point to next
- mov al,ah ;get low byte again
- mov cl,4 ;set counter
- shr al,cl ;shift high nibble to low nibble
- xlat hex_tbl ;translate hex to ascii
- mov [si],al ;store 2nd/4th digit
- dec si ;point to next
- ret
-
- hex_tbl db "0123456789ABCDEF" ;hex to ascii conversion
-
- hex_lookup endp
-
- ;***** Program End *****************************************************
-
- prog_end label byte ;label the end of the program
-
- code ends
-
- end start
-